//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <bulverde.h>
#include <drvpub.h>
#include "audiocard.h"
#include "ac97codec.h"
#include "ac97link.h"

#define PAC_REG_BASE	0x80500200   /* Primary Audio Codec */
#define SAC_REG_BASE    0x80500300  /* Secondary Audio Codec */

static DzMutex   s_lock;

// Try to lock the AC Link for command/status accesses to a codec.
ECode Ac97LinkLock()
{
    BOOL	status = NOERROR;
    UINT	carTmp;

    carTmp = CAR;
    if (carTmp & XLLP_AC97_CAR_CAIP_MSK)	// "1" in CAIP bit means lock failed.
    {
        status = E_ACCESS_DENIED;
    }
    return (status);
}

void  Ac97Lock()
{
    WaitResult wr;
    return s_lock.Lock(&wr);
}

void  Ac97Unlock()
{
	return s_lock.Unlock();
}


ECode  RawAc97Read(USHORT offset,
								USHORT *pdata,
								UINT maxRWTimeOutUs,
								XLLP_AC97_CODEC_SEL_T codecSel)
{
    ECode	status = NOERROR;
    BOOL	gotLink;
	UINT	timeRemaining;
    volatile UINT*	pCodecReg;


	if (XLLP_AC97_CODEC_PRIMARY == codecSel){
		pCodecReg = (UINT*)PAC_REG_BASE;
	}
	else{
		pCodecReg = (UINT*)SAC_REG_BASE;
	}
	pCodecReg += (offset>>1);

	//Lock the ACLINK
    timeRemaining = XLLP_AC97_LOCK_TIMEOUT_DEF;
    do
    {
        gotLink = Ac97LinkLock();
        if (FAILED(gotLink))	// 1 usec is a long time.  Skip delay if possible.
        {
            DzDelay(100);    //
        }
    }        // Wait while time remaining and ACLINK not available
    while (timeRemaining-- && (FAILED(gotLink)));

    if (FAILED(gotLink))	// Didn't get the ACLINK
    {
        status = E_ACCESS_DENIED;
    }
    else	// We got the link. Perform the write operation and don't wait.
    {
         // First, clear old read status indications.
        GSR = XLLP_AC97_GSR_SDONE_MSK | XLLP_AC97_GSR_RCS_ERR_MSK;
        //may error
        // This is THE DUMMY READ.
        *pdata = (USHORT)(*pCodecReg);

         // Wait for read I/O with codec to complete before doing real read.
        timeRemaining = maxRWTimeOutUs;
        do
        {
            DzDelay(100);
        }   // Wait while time remaining and read I/O still incomplete
        while( (timeRemaining--) && (!(GSR & XLLP_AC97_GSR_SDONE_MSK)) );

        if ((GSR & XLLP_AC97_GSR_SDONE_MSK) && (!(GSR & XLLP_AC97_GSR_RCS_ERR_MSK)) )
        {
             // succeed in reading. clear status bits first.
            GSR = XLLP_AC97_GSR_SDONE_MSK | XLLP_AC97_GSR_RCS_ERR_MSK;
            *pdata = (USHORT)(*pCodecReg);	// THE REAL READ.

     		timeRemaining = maxRWTimeOutUs;
    	    do
	        {
                DzDelay(100);
  		    }   // Wait while time remaining and read I/O still incomplete
    	    while( (timeRemaining--) && (!(GSR & XLLP_AC97_GSR_SDONE_MSK)) );
        }
        else	// failed
        {
            status = E_ACCESS_DENIED;
            CAR = XLLP_AC97_CAR_CAIP_CLEAR;
        } // else  (OK to do real read)
    } // else  (We got the link.  Perform the read operations.)

    return (status);
}

ECode  RawAc97Write(USHORT offset,
								 USHORT data,
								 UINT maxRWTimeOutUs,
								 XLLP_AC97_CODEC_SEL_T codecSel)
{
    ECode	status = NOERROR;
    BOOL	gotLink;
    UINT	timeRemaining;
    UINT*	pCodecReg;

	if (XLLP_AC97_CODEC_PRIMARY == codecSel){
		pCodecReg = (UINT*)PAC_REG_BASE;
	}
	else{
		pCodecReg = (UINT*)SAC_REG_BASE;
	}
	pCodecReg += (offset>>1);

	//Lock the ACLINK
    timeRemaining = XLLP_AC97_LOCK_TIMEOUT_DEF;
    do
    {
        gotLink = Ac97LinkLock();
        if (FAILED(gotLink))	// 1 usec is a long time.  Skip delay if possible.
        {
            DzDelay(10*100);
        }
    }        // Wait while time remaining and ACLINK not available
    while (timeRemaining-- && (FAILED(gotLink)));

    if (FAILED(gotLink))	// Didn't get the ACLINK
    {
        status = E_ACCESS_DENIED;
    }
    else	// We got the link. Perform the write operation and don't wait.
    {
        // First, clear old write status indication CDONE by writing a ONE to that bit.
        GSR = XLLP_AC97_GSR_CDONE_MSK;

        *pCodecReg = (UINT)data;       // Now the write!
        //*pCodecReg = (XLLP_VUINT32_T)data;
        // Wait until write cycle is complete. There should be a way
        //  to do this speculatively at the beginning of the procedure.
        //  Need to discover it. Too inefficient to always wait.

        timeRemaining = maxRWTimeOutUs;
        do
        {
            DzDelay(10*100);
        }     // Wait while time remaining and command I/O still incomplete.

        while ( (timeRemaining--) && !(GSR & XLLP_AC97_GSR_CDONE_MSK));
		if (!(GSR & XLLP_AC97_GSR_CDONE_MSK)){
			status = E_ACCESS_DENIED;
		}
    }  // Got AC link

    return(status);
}

ECode Ac97_Read(UCHAR Offset, USHORT * Data)
{
	ECode retval = NOERROR;

	Ac97Lock();

	retval = RawAc97Read((USHORT)Offset, (USHORT *)Data,
								 5000,
								 XLLP_AC97_CODEC_PRIMARY) ;;

	Ac97Unlock();

	return(retval);
}


ECode  Ac97_Write(UCHAR Offset, USHORT  Data)
{
	ECode retval = NOERROR;

	Ac97Lock();

	retval = RawAc97Write((USHORT) Offset , (USHORT) Data,
								 5000,
								 XLLP_AC97_CODEC_PRIMARY);

	Ac97Unlock();

	return(retval);
}

#define GCR_COLD_RST	(1 << 1)	/* AC'97 Cold Reset (0 = active) */
#define GSR_PCR		(1 << 8)	/*  Primary Codec Ready */
void Ac97_Reset(AudioContext *pCxt)
{
    // ???
    CKEN |= 0x80000000;
    DzDelay(100);
    CKEN &= ~(0x80000000);
    DzDelay(100);

    GCR = 0;
	DzDelay(500);

    // Activate a cold reset
    GCR &= ~GCR_COLD_RST;
    DzDelay(10000);
    GCR |= GCR_COLD_RST;// | GCR_CDONE_IE | GCR_SDONE_IE ;
	DzDelay(100);
    GCR |= 0x04;
    DzDelay(100);

    GPSR3 |= 1 << 17;   // GPIO113, workaround to reset the code
    DzDelay(30);

    while (!(GSR & GSR_PCR)) {
        // Sleep
    }
}
